package triCompact;


import java.util.concurrent.locks.*;
import transaction.*;
import triBasic.*;
import triInstance.*;
import triTree.*;

public class CompSetMgr implements ISetManager{

	private ISetManager oldSetMgr;
	private SetManager newSetMgr;
	
	private int maxOldSetId;
	private int curSetId;
	
	private ReadWriteLock transSetLock;
	private Lock transSetRlock;
	private Lock transSetWlock;
	
	private Object accessLock;
	
	public CompSetMgr(ISetManager setMgr, int set_buffersize, String filename, int pageBufferSize, int pagesize,
			String inode_filename, int inode_pageBufferSize, int inode_pagesize) {
		this.oldSetMgr = setMgr;
		this.newSetMgr = new SetManager(set_buffersize, filename, pageBufferSize, pagesize,
				inode_filename, inode_pageBufferSize, inode_pagesize);
		
		this.maxOldSetId = oldSetMgr.createNewSet();
		System.out.println(maxOldSetId);
		this.curSetId = 0;
		//this.curSetId = 1;
		
		transSetLock = new ReentrantReadWriteLock();
		transSetRlock = transSetLock.readLock();
		transSetWlock = transSetLock.writeLock();
		accessLock = new Object();
	}
	
	public void init(Transaction tx) {
		this.newSetMgr.init(tx);
		
		for (int i = 0; i < maxOldSetId; ++i)
			newSetMgr.createNewSet();
	}
	
	public ISetManager compact(TxManager txMgr) {
		Transaction tx;
		ISet<String> set;
		String str;
		NodeSnapshot snapshot;
		
		System.out.println("compact " + curSetId + " " + this.maxOldSetId);
		
		while (true) {
			tx = txMgr.newTransaction();
			
			synchronized (accessLock) {
				System.out.println("curSetId " + curSetId);
				snapshot = new NodeSnapshot(0, "", 0, this.curSetId);
				set = oldSetMgr.queryTriSet(snapshot, tx);
			
				while (true) {
					str = set.nextItem();
					if (str == null)
						break;
					newSetMgr.insertItem(snapshot, str, tx);
				}
				
				transSetWlock.lock();
				++curSetId;
				transSetWlock.unlock();
			}

			tx.commit();
			
			if (curSetId >= this.maxOldSetId)
				break;
		}
		
		return newSetMgr;
	}
	
	@Override
	public int createNewSet() {
		return newSetMgr.createNewSet();
	}
	
	@Override
	public void insertItem(NodeSnapshot snapshot, String item, Transaction tx) {
		int inodePageId = snapshot.getInodePageId();
		
		transSetRlock.lock();
		if (this.curSetId == inodePageId) {
			transSetRlock.unlock();
			
			synchronized (accessLock) {
				if (this.curSetId == inodePageId) 
					oldSetMgr.insertItem(snapshot, item, tx);
				else
					newSetMgr.insertItem(snapshot, item, tx);
			}
		}
		else if (this.curSetId < inodePageId && inodePageId < this.maxOldSetId) {
			oldSetMgr.insertItem(snapshot, item, tx);
			transSetRlock.unlock();
		}
		else {
			//System.out.println("compact insert into new " + inodePageId + " " + curSetId + " " + maxOldSetId);
			transSetRlock.unlock();
			newSetMgr.insertItem(snapshot, item, tx);
		}
	}
	
	@Override
	public void removeItem(NodeSnapshot snapshot, String item, Transaction tx) {
		int inodePageId = snapshot.getInodePageId();
		
		transSetRlock.lock();
		if (this.curSetId == inodePageId) {
			transSetRlock.unlock();
			
			synchronized (accessLock) {
				if (this.curSetId == inodePageId) 
					oldSetMgr.removeItem(snapshot, item, tx);
				else
					newSetMgr.removeItem(snapshot, item, tx);
			}
		}
		else if (this.curSetId < inodePageId && inodePageId < this.maxOldSetId) {
			oldSetMgr.removeItem(snapshot, item, tx);
			transSetRlock.unlock();
		}
		else {
			transSetRlock.unlock();
			newSetMgr.removeItem(snapshot, item, tx);
		}
	}
	
	@Override
	public ISet<String> queryTriSet(NodeSnapshot snapshot, Transaction tx) {
		int inodePageId = snapshot.getSetInodePageId();
		ISet<String> value;
		
		transSetRlock.lock();
		if (this.curSetId == inodePageId) {
			transSetRlock.unlock();
			
			synchronized (accessLock) {
				if (this.curSetId == inodePageId) 
					value = oldSetMgr.queryTriSet(snapshot, tx);
				else
					value = newSetMgr.queryTriSet(snapshot, tx);
			}
		}
		else if (this.curSetId < inodePageId && inodePageId < this.maxOldSetId) {
			value = oldSetMgr.queryTriSet(snapshot, tx);
			transSetRlock.unlock();
		}
		else {
			transSetRlock.unlock();
			value = newSetMgr.queryTriSet(snapshot, tx);
		}
		
		return value;
	}
	
	public void dispose(Transaction tx) {
		oldSetMgr.dispose(tx);
		newSetMgr.dispose(tx);
	}
		
}
